package agency.tango.materialintroscreen.slice;

import java.io.IOException;
import java.util.concurrent.ConcurrentLinkedDeque;

import static ohos.agp.components.Component.VISIBLE;

import agency.tango.materialintroscreen.SlideComponentContainer;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Image;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentTreeObserver;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.VectorElement;
import ohos.agp.utils.Color;
import ohos.bundle.IBundleManager;
import ohos.event.commonevent.*;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.multimodalinput.event.KeyEvent;
import ohos.rpc.RemoteException;
import ohos.utils.LongPlainArray;

import agency.tango.materialintroscreen.MessageButtonBehaviour;
import agency.tango.materialintroscreen.adapter.SlidesAdapter;
import agency.tango.materialintroscreen.animations.ViewTranslationWrapper;
import agency.tango.materialintroscreen.animations.wrappers.BackButtonTranslationWrapper;
import agency.tango.materialintroscreen.animations.wrappers.NextButtonTranslationWrapper;
import agency.tango.materialintroscreen.animations.wrappers.PageIndicatorTranslationWrapper;
import agency.tango.materialintroscreen.animations.wrappers.SkipButtonTranslationWrapper;
import agency.tango.materialintroscreen.animations.wrappers.ViewPagerTranslationWrapper;
import agency.tango.materialintroscreen.listeners.IPageSelectedListener;
import agency.tango.materialintroscreen.listeners.IPageScrolledListener;
import agency.tango.materialintroscreen.listeners.ViewBehavioursOnPageChangeListener;
import agency.tango.materialintroscreen.listeners.clickListeners.PermissionNotGrantedClickListener;
import agency.tango.materialintroscreen.listeners.scrollListeners.ParallaxScrollListener;
import agency.tango.materialintroscreen.listeners.MessageButtonBehaviourOnPageSelected;
import agency.tango.materialintroscreen.utils.ArgbEvaluator;
import agency.tango.materialintroscreen.utils.ElementUtil;
import agency.tango.materialintroscreen.utils.LogUtil;
import agency.tango.materialintroscreen.widgets.BottomSnackBar;
import agency.tango.materialintroscreen.widgets.InkPageIndicator;
import agency.tango.materialintroscreen.widgets.OverScrollViewPager;
import agency.tango.materialintroscreen.widgets.SwipeableViewPager;
import agency.tango.materialintroscreen.Constants;
import agency.tango.materialintroscreen.ResourceTable;

/**
 * @ProjectName: material-intro-screen-master
 * @Package: agency.tango.materialintroscreen.slice
 * @ClassName: MaterialIntroSlice
 * @Description: 项目介绍页面基类 主要处理pageslider控件初始化 指示器初始化  子条目的添加
 * 子条目的滑动事件监听器的注册 按钮Behaviour的监听注册 控件位移监听器的注册 pageslider指示器监听器的注册
 * @CreateDate: 2021/4/12 17:18
 * @UpdateDate: 2021/4/12 17:18
 * @UpdateRemark: 暂无
 * @Version: 1.0
 */
public class MaterialIntroSlice extends AbilitySlice {
    private static final ConcurrentLinkedDeque<Runnable> HAND_QUENE =
            new ConcurrentLinkedDeque<>();
    private static final Object LOCK = new Object();
    private static final long SNACKBAR_DURATION = 2000;
    private long nextClickTime = 0; // 防止过快点击出现BUG
    private SwipeableViewPager viewPager;
    private InkPageIndicator pageIndicator;
    private SlidesAdapter adapter;
    private Image backButton;
    private Image skipButton;
    private Image nextButton;
    private Button messageButton;
    private DirectionalLayout navigationView;
    private OverScrollViewPager overScrollLayout;
    private final ArgbEvaluator<Integer> argbEvaluator = new ArgbEvaluator<>();
    private ViewTranslationWrapper nextButtonTranslationWrapper;
    private ViewTranslationWrapper backButtonTranslationWrapper;
    private ViewTranslationWrapper pageIndicatorTranslationWrapper;
    private ViewTranslationWrapper viewPagerTranslationWrapper;
    private ViewTranslationWrapper skipButtonTranslationWrapper;
    private MessageButtonBehaviourOnPageSelected messageButtonBehaviourOnPageSelected;
    private Component.ClickedListener permissionNotGrantedClickListener;
    private Component.ClickedListener finishScreenClickListener;
    private LongPlainArray<MessageButtonBehaviour> messageButtonBehaviours =
            new LongPlainArray<>();
    private BottomSnackBar snackBar;
    private MyCommonEventSubscriber subscriber;

    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_slice_material_intro_slice);
        if (messageButtonBehaviours == null) {
            messageButtonBehaviours = new LongPlainArray();
        }
        messageButtonBehaviours.clear();
        initView();
    }


    /**
     * 初始化控件及注册监听器
     */
    private void initView() {
        overScrollLayout = (OverScrollViewPager) findComponentById(ResourceTable.Id_view_pager_slides);
        viewPager = overScrollLayout.getOverScrollView();
        pageIndicator = (InkPageIndicator) findComponentById(ResourceTable.Id_indicator);
        backButton = (Image) findComponentById(ResourceTable.Id_button_back);
        nextButton = (Image) findComponentById(ResourceTable.Id_button_next);
        skipButton = (Image) findComponentById(ResourceTable.Id_button_skip);
        messageButton = (Button) findComponentById(ResourceTable.Id_button_message);
        navigationView = (DirectionalLayout) findComponentById(ResourceTable.Id_navigation_view);
        adapter = new SlidesAdapter();
        viewPager.setProvider(adapter);
        viewPager.setPageCacheSize(1);
        pageIndicator.setViewPager(viewPager);
        nextButtonTranslationWrapper = new NextButtonTranslationWrapper(nextButton);
        initOnPageChangeListeners();
        permissionNotGrantedClickListener = new PermissionNotGrantedClickListener(this,
                nextButtonTranslationWrapper);
        finishScreenClickListener = new FinishScreenClickListener();
        setBackButtonVisible();

        viewPager.getComponentTreeObserver().addWindowBoundListener(
                new ComponentTreeObserver.WindowBoundListener() {
                    @Override
                    public void onWindowBound() {
                        for (int i = (HAND_QUENE.size() - 1); i >= 0; i--) {
                            postMainTask(HAND_QUENE.pollFirst());
                        }
                    }

                    @Override
                    public void onWindowUnbound() {
                        // do nothing
                    }
                });
        initBottomBar();
        refresh();
        registPermissionResultSubcriber();
    }

    private void initBottomBar() {
        BottomSnackBar.Builder builder =
                new BottomSnackBar.Builder(getContext());
        int color = ElementUtil.getColor(getContext(),
                ResourceTable.Color_bottom_snackbar_background_color);
        snackBar = builder.setSnackBarContent("")
                .setTextColor(new Color(ElementUtil.getColor(getContext(),
                        ResourceTable.Color_bottom_snackbar_text_color)))
                .setBackGroundColor(new RgbColor(ElementUtil.alphaColor(color, 0.5f)))
                .setTextSize(14)
                .setDuration(SNACKBAR_DURATION)
                .setNeedShowBar(messageButton != null && messageButton.getVisibility() == VISIBLE)
                .setAnimationDuration(50)
                .build();
        if (permissionNotGrantedClickListener instanceof PermissionNotGrantedClickListener) {
            ((PermissionNotGrantedClickListener) permissionNotGrantedClickListener).setSnackBar(snackBar);
        }
    }

    private void refresh() {
        Runnable task = () -> {
            if (adapter.getCount() == 0) {
                terminate();
            } else {
                int currentItem = viewPager.getCurrentPage();
                messageButtonBehaviourOnPageSelected.pageSelected(currentItem);
                nextButtonBehaviour(currentItem, adapter.getItem(currentItem));

                // 安卓viewpager初始化会调一次onpagescroll 该方法会调用到本页面的setViewsColor
                // 以及IntroSlice的getBackButtonTranslationWrapper.setEnterTranslation里面的translate方法
                // 但是pageslide不会回调onPageSliding  所以这里在view显示出来之后主动调用
                setViewsColor(0, 0.0001f);
                backButton.setAlpha(0);
            }

        };
        dealMainTak(task);
    }

    /**
     * 页面加载未完成之前的刷新界面的任务先加入到无限双端队列
     * 加载完成之后则发送到主线程handler去处理 刷新界面
     *
     * @param task 线程任务
     */
    private void dealMainTak(Runnable task) {
        if (task == null || viewPager == null) {
            return;
        }
        synchronized (LOCK) {
            if (viewPager.isBoundToWindow()) {
                postMainTask(task);
            } else {
                if (!HAND_QUENE.contains(task)) {
                    HAND_QUENE.add(task);
                }
            }
        }
    }

    /**
     * 线程任务发送到主线程的handler
     *
     * @param task 线程任务
     */
    private void postMainTask(Runnable task) {
        EventRunner mainEventRunner = EventRunner.getMainEventRunner();
        EventHandler handler = new EventHandler(mainEventRunner);
        handler.postTask(task);
    }

    private void initOnPageChangeListeners() {
        messageButtonBehaviourOnPageSelected =
                new MessageButtonBehaviourOnPageSelected(messageButton,
                        adapter, messageButtonBehaviours);
        backButtonTranslationWrapper =
                new BackButtonTranslationWrapper(backButton);
        pageIndicatorTranslationWrapper =
                new PageIndicatorTranslationWrapper(pageIndicator);
        viewPagerTranslationWrapper =
                new ViewPagerTranslationWrapper(viewPager);
        skipButtonTranslationWrapper =
                new SkipButtonTranslationWrapper(skipButton);
        overScrollLayout.registerFinishListener(this::performFinish);
        viewPager.addOnPageChangeListeners(new ViewBehavioursOnPageChangeListener(adapter)
                .registerViewTranslationWrapper(nextButtonTranslationWrapper)
                .registerViewTranslationWrapper(backButtonTranslationWrapper)
                .registerViewTranslationWrapper(pageIndicatorTranslationWrapper)
                .registerViewTranslationWrapper(viewPagerTranslationWrapper)
                .registerViewTranslationWrapper(skipButtonTranslationWrapper)
                .registerOnPageScrolled((position, offset) -> {
                    Runnable runnable = () -> {
                        if (adapter.getItem(position).hasNeededPermissionsToGrant()
                                || !adapter.getItem(position).canMoveFurther()) {
                            viewPager.setCurrentPage(position, true);
                            pageIndicator.clearJoiningFractions();
                        }
                    };
                    dealMainTak(runnable);
                })
                .registerOnPageScrolled(new ColorTransitionScrollListener())
                .registerOnPageScrolled(new ParallaxScrollListener(adapter))
                .registerPageSelectedListener(messageButtonBehaviourOnPageSelected)
                .registerPageSelectedListener(position -> {
                    nextButtonBehaviour(position, adapter.getItem(position));
                    if (adapter.shouldFinish(position)) {
                        performFinish();
                    }
                }));
    }

    /**
     * 显示无权限错误信息
     */
    public void showPermissionsNotGrantedError() {
        try {
            showError(getContext().getResourceManager()
                    .getElement(ResourceTable.String_please_grant_permissions).getString());
        } catch (NotExistException | WrongTypeException | IOException e) {
            LogUtil.error(this.getClass().getSimpleName(), e.toString());
        }
    }

    /**
     * Set skip button instead of back button
     */
    public void setSkipButtonVisible() {
        skipButton.setVisibility(Component.HIDE);
        skipButton.setVisibility(VISIBLE);
        skipButton.setClickedListener(view -> {
            for (int currentPosition = adapter.getCount();
                 currentPosition < adapter.getCount(); currentPosition++) {
                if (!adapter.getItem(currentPosition).canMoveFurther()) {
                    viewPager.setCurrentPage(currentPosition, true);
                    showError(adapter.getItem(currentPosition).cantMoveFurtherErrorMessage());
                    return;
                }
            }
            viewPager.setCurrentPage(adapter.getLastItemPosition(), true);
        });
    }

    /**
     * Set back button visible
     */
    public void setBackButtonVisible() {
        skipButton.setVisibility(Component.HIDE);
        backButton.setVisibility(VISIBLE);
        backButton.setClickedListener(view -> viewPager.setCurrentPage(viewPager.getPreviousItem(), true));
    }

    /**
     * Add SlideFragment to IntroScreen
     *
     * @param slideFragment Fragment to add
     */
    public void addSlide(SlideComponentContainer slideFragment) {
        adapter.addItem(slideFragment);
    }

    /**
     * Add SlideFragment to IntroScreen
     *
     * @param slideFragment          Fragment to add
     * @param messageButtonBehaviour Add behaviour for message button
     */
    public void addSlide(SlideComponentContainer slideFragment, MessageButtonBehaviour messageButtonBehaviour) {
        adapter.addItem(slideFragment);
        messageButtonBehaviours.put(adapter.getLastItemPosition(),
                messageButtonBehaviour);
    }

    /**
     * Hides any back button
     */
    public void hideBackButton() {
        backButton.setVisibility(Component.INVISIBLE);
        skipButton.setVisibility(Component.HIDE);
    }

    /**
     * Get translation wrapper for next button
     *
     * @return ViewTranslationWrapper
     */
    public ViewTranslationWrapper getNextButtonTranslationWrapper() {
        return nextButtonTranslationWrapper;
    }

    /**
     * Get translation wrapper for back button
     *
     * @return ViewTranslationWrapper
     */
    public ViewTranslationWrapper getBackButtonTranslationWrapper() {
        return backButtonTranslationWrapper;
    }

    /**
     * Get translation wrapper for page indicator
     *
     * @return ViewTranslationWrapper
     */
    public ViewTranslationWrapper getPageIndicatorTranslationWrapper() {
        return pageIndicatorTranslationWrapper;
    }

    /**
     * Get translation wrapper for view pager
     *
     * @return ViewTranslationWrapper
     */
    public ViewTranslationWrapper getViewPagerTranslationWrapper() {
        return viewPagerTranslationWrapper;
    }

    /**
     * Get translation wrapper for skip button
     *
     * @return ViewTranslationWrapper
     */
    public ViewTranslationWrapper getSkipButtonTranslationWrapper() {
        return skipButtonTranslationWrapper;
    }

    /**
     * Set if last screen should be able to exit with alpha transition
     *
     * @param enableAlphaExitTransition should enable alpha exit transition
     */
    public void enableLastSlideAlphaExitTransition(boolean enableAlphaExitTransition) {
        viewPager.alphaExitTransitionEnabled(enableAlphaExitTransition);
    }

    /**
     * Show snackbar message
     *
     * @param message Message which will be visible to user
     */
    public void showMessage(String message) {
        showError(message);
    }

    private void moveBack() {
        if (viewPager.getCurrentPage() == 0) {
            terminate();
        } else {
            viewPager.setCurrentPage(viewPager.getPreviousItem(), true);
        }
    }

    private void nextButtonBehaviour(final int position, final SlideComponentContainer slideComponet) {
        boolean hasPermissionToGrant = slideComponet.hasNeededPermissionsToGrant();
        if (hasPermissionToGrant) {
            nextButton.setImageElement(new VectorElement(getContext(),
                    ResourceTable.Graphic_ic_next));
            nextButton.setClickedListener(permissionNotGrantedClickListener);
        } else if (adapter.isLastSlide(position)) {
            nextButton.setImageElement(new VectorElement(this,
                    ResourceTable.Graphic_ic_finish));
            nextButton.setClickedListener(finishScreenClickListener);
        } else {
            nextButton.setImageElement(new VectorElement(this,
                    ResourceTable.Graphic_ic_next));
            nextButton.setClickedListener(view -> {
                doNextButtonClick(slideComponet);
            });
        }
    }

    private void doNextButtonClick(final SlideComponentContainer slideComponet) {
        if (slideComponet.canMoveFurther() == false) {
            if (System.currentTimeMillis() - nextClickTime <= SNACKBAR_DURATION
                    || (snackBar != null && snackBar.isShow())) {
                return;
            }
            nextClickTime = System.currentTimeMillis();
            errorOccurred(slideComponet);
        } else {
            viewPager.moveToNextPage();
        }

    }

    private class FinishScreenClickListener implements Component.ClickedListener {
        private long clickTime = 0;

        @Override
        public void onClick(Component view) {
            if (System.currentTimeMillis() - clickTime <= SNACKBAR_DURATION
                    || (snackBar != null && snackBar.isShow())) {
                return;
            }
            clickTime = System.currentTimeMillis();
            SlideComponentContainer slideFragment =
                    adapter.getItem(adapter.getLastItemPosition());
            if (!slideFragment.canMoveFurther()) {
                errorOccurred(slideFragment);
            } else {
                performFinish();
            }
        }
    }

    private Integer getBackgroundColor(int position, float positionOffset) {
        return (Integer) argbEvaluator.evaluate(positionOffset,
                color(adapter.getItem(position).backgroundColor()),
                color(adapter.getItem(position + 1).backgroundColor()));
    }

    private Integer getButtonsColor(int position, float positionOffset) {
        return argbEvaluator.evaluate(positionOffset,
                color(adapter.getItem(position).buttonsColor()),
                color(adapter.getItem(position + 1).buttonsColor()));
    }

    private int color(int color) {
        return ElementUtil.getColor(getContext(), color);
    }

    private void errorOccurred(SlideComponentContainer slideFragment) {
        nextButtonTranslationWrapper.error();
        showError(slideFragment.cantMoveFurtherErrorMessage());
    }

    private void performFinish() {
        onFinish();
        terminate();
    }

    /**
     * Override to execute this method on finish intro activity
     */
    public void onFinish() {
    }

    private void showError(String error) {
        snackBar.getBuilder()
                .setSnackBarContent(error)
                .setNeedShowBar(true);
        snackBar.show(navigationView);
    }

    private class ColorTransitionScrollListener implements IPageScrolledListener, IPageSelectedListener {
        @Override
        public void pageSelected(int position) {
            int backgroundColor = adapter.getItem(position).backgroundColor();
            ShapeElement element = new ShapeElement();
            element.setRgbColor(new RgbColor(ElementUtil.alphaColor(backgroundColor, 1.0f)));
            viewPager.setBackground(element);
        }

        @Override
        public void pageScrolled(int position, float offset) {
            if (position < adapter.getCount() - 1) {
                setViewsColor(position, offset);
            } else if (adapter.getCount() == 1) {
                int backgroundColor = getBackgroundColor(position, offset);
                ShapeElement element = new ShapeElement();
                element.setRgbColor(new RgbColor(ElementUtil.alphaColor(backgroundColor, 1.0f)));
                viewPager.setBackground(element);
                messageButton.setTextColor(new Color(ElementUtil.getColor(getContext(),
                        adapter.getItem(position).backgroundColor())));
                tintButtons(adapter.getItem(position).buttonsColor());
            } else {
                LogUtil.info(this.getClass().getSimpleName(), "pageScrolled");
            }
        }


    }

    /**
     * 设置当前页面
     *
     * @param page 当前页面
     */
    protected void setCurrentPage(int page) {
        if (viewPager != null) {
            viewPager.setCurrentPage(page);
        }
    }

    private void setViewsColor(int position, float offset) {
        int backgroundColor = getBackgroundColor(position, offset);
        ShapeElement element = new ShapeElement();
        element.setRgbColor(new RgbColor(ElementUtil.alphaColor(backgroundColor, 1.0f)));
        viewPager.setBackground(element);
        messageButton.setTextColor(new Color(backgroundColor));
        int buttonsColor = getButtonsColor(position, offset);
        if (getWindow() != null) {
            getWindow().setStatusBarColor(backgroundColor);
        }
        pageIndicator.setPageIndicatorColor(buttonsColor);

        tintButtons(buttonsColor);
    }

    private void tintButtons(int color) {
        ShapeElement element = new ShapeElement();
        element.setRgbColor(new RgbColor(ElementUtil.alphaColor(color, 1.0f)));
        element.setShape(ShapeElement.OVAL);
        nextButton.setBackground(element);
        backButton.setBackground(element);
        skipButton.setBackground(element);
    }

    /**
     * 注冊权限申请结果监听
     */
    private void registPermissionResultSubcriber() {
        try {
            MatchingSkills matchSkills = new MatchingSkills();
            matchSkills.addEvent(Constants.PERMISSION_ACTION);  // 自定义事件
            CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchSkills);
            subscriber = new MyCommonEventSubscriber(subscribeInfo);
            CommonEventManager.subscribeCommonEvent(subscriber);
        } catch (RemoteException e) {
            LogUtil.error("SlideComponentContainer", e.toString());
        }
    }

    /**
     * 权限申请完毕之后通知刷新nextButton
     */
    private void refreshNextButtonBehavior() {
        if (viewPager == null || adapter == null) {
            return;
        }
        int currentPage = viewPager.getCurrentPage();
        if (currentPage < 0 || currentPage >= adapter.getCount() || adapter.getItem(currentPage) == null) {
            return;
        }
        nextButtonBehaviour(currentPage, adapter.getItem(currentPage));
    }

    /**
     * 权限申请完毕之后通知刷新MessageButton
     */
    private void refreshMessageButtonBehavior() {
        if (viewPager == null || messageButtonBehaviourOnPageSelected == null) {
            return;
        }
        messageButtonBehaviourOnPageSelected.pageSelected(viewPager.getCurrentPage());
    }

    /**
     * 权限申请完毕之后通知刷新MessageButton
     */

    public class MyCommonEventSubscriber extends CommonEventSubscriber {
        public MyCommonEventSubscriber(CommonEventSubscribeInfo subscribeInfo) {
            super(subscribeInfo);
        }

        @Override
        public void onReceiveEvent(CommonEventData eventData) {
            if (eventData == null || eventData.getIntent() == null) {
                return;
            }
            Intent intent = eventData.getIntent();
            if (intent.getAction() == null || !intent.getAction().equals(Constants.PERMISSION_ACTION)) {
                return;
            }
            int[] grantResults = intent.getIntArrayParam(Constants.GRANT_RESULT);
            if (grantResults == null) {
                grantResults = new int[]{};
            }
            boolean isAllGrant = true;
            for (int i = 0; i < grantResults.length; i++) {
                if (grantResults[i] != IBundleManager.PERMISSION_GRANTED) {
                    isAllGrant = false;
                }
            }
            if (!isAllGrant) {
                MaterialIntroSlice.this.showPermissionsNotGrantedError();
            } else {
                MaterialIntroSlice.this.refreshNextButtonBehavior();
                MaterialIntroSlice.this.refreshMessageButtonBehavior();
            }
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
        switch (keyCode) {
            case KeyEvent.KEY_DPAD_CENTER:
                if (messageButtonBehaviours.get(viewPager.getCurrentPage()) != null) {
                    messageButton.callOnClick();
                }
                break;
            case KeyEvent.KEY_DPAD_RIGHT:
                int position = viewPager.getCurrentPage();
                if (adapter.isLastSlide(position) && adapter.getItem(position).canMoveFurther()) {
                    performFinish();
                } else if (adapter.shouldLockSlide(position)) {
                    errorOccurred(adapter.getItem(position));
                } else {
                    viewPager.moveToNextPage();
                }
                break;
            case KeyEvent.KEY_DPAD_LEFT:
                moveBack();
                break;
            default:
                return super.onKeyDown(keyCode, keyEvent);
        }
        return super.onKeyDown(keyCode, keyEvent);
    }

    @Override
    protected void onBackPressed() {
       moveBack();
    }
}
